home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 February: Technology Seed / Mac Tech Seed Feb '97.toast / OpenDoc 1.2b2c1 / Implementation / Storage / ScriptID.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1997-02-13  |  26.8 KB  |  1,009 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        ScriptID.cpp
  3.  
  4.     Contains:    Implementation of ScriptingIDManager.
  5.  
  6.     Owned by:    Craig Carper
  7.  
  8.     Copyright:    © 1996 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     Change History (most recent first):
  11.  
  12.          <1>      11/19/96    CC        first checked in
  13.     To Do:
  14. */
  15.  
  16. #ifndef _ScriptID_
  17. #include "ScriptID.h"
  18. #endif
  19.  
  20. #ifndef SOM_CMStorageUnit_xh
  21. #include <CMSU.xh>
  22. #endif
  23.  
  24. #ifndef _CMAPI_
  25. #include "CMAPI.h"
  26. #endif
  27.  
  28. #ifndef _SESSHDR_
  29. #include "SessHdr.h"
  30. #endif
  31.  
  32. #ifndef _LINKLIST_
  33. #include "LinkList.h"
  34. #endif
  35.  
  36. #ifndef SOM_ODFrame_xh
  37. #include <Frame.xh>
  38. #endif
  39.  
  40. #ifndef _ODMEMORY_
  41. #include <ODMemory.h>
  42. #endif
  43.  
  44. #ifndef _TEMPOBJ_
  45. #include <TempObj.h>
  46. #endif
  47.  
  48. #ifndef SOM_Module_OpenDoc_StdProps_defined
  49. #include <StdProps.xh>
  50. #endif
  51.  
  52. #ifndef SOM_Module_OpenDoc_StdTypes_defined
  53. #include <StdTypes.xh>
  54. #endif
  55.  
  56. #ifndef _ODDEBUG_
  57. #include "ODDebug.h"
  58. #endif
  59.  
  60. #ifndef _STORUTIL_
  61. #include <StorUtil.h>
  62. #endif
  63.  
  64. #ifndef _ISOSTR_
  65. #include "ISOStr.h"
  66. #endif
  67.  
  68. #ifndef _FLIPEND_
  69. #include "FlipEnd.h"
  70. #endif
  71.  
  72. #ifndef _STDTYPIO_
  73. #include "StdTypIO.h"
  74. #endif
  75.  
  76. //==============================================================================
  77. // Debugging
  78. //==============================================================================
  79.  
  80. #if ODDebug
  81. #define ODDebug_ScriptingIDs    1
  82. #endif
  83.  
  84. //==============================================================================
  85. // Constants
  86. //==============================================================================
  87.  
  88. const int kODULongLen = 4;
  89.  
  90. const int kODInitialNumEntries = 10;
  91.  
  92. const long kFirstCMObjectID = 65535;    // Used for sanity checks
  93.  
  94. //==============================================================================
  95. // Local Functions
  96. //==============================================================================
  97.  
  98. static void WriteULongToFocusedSU(ODStorageUnit* su,
  99.                 Environment* ev, 
  100.                 ODULong value);
  101.                 
  102. static ODULong GetULongFromFocusedSU(ODStorageUnit* su, Environment* ev);
  103.  
  104. //------------------------------------------------------------------------------
  105. // WriteULongToFocusedSU
  106. //------------------------------------------------------------------------------
  107.  
  108. static void WriteULongToFocusedSU(ODStorageUnit* su,
  109.                 Environment* ev, 
  110.                 ODULong value)
  111. {
  112.     value = ConvertODULongToStd(value);
  113.     StorageUnitSetValue(su, ev, kODULongLen, (ODValue) &value);
  114. }
  115.  
  116. //------------------------------------------------------------------------------
  117. // GetULongFromFocusedSU
  118. //------------------------------------------------------------------------------
  119.  
  120. static ODULong GetULongFromFocusedSU(ODStorageUnit* su, Environment* ev)
  121. {
  122.     ODULong value;
  123.     StorageUnitGetValue(su, ev, kODULongLen, (ODValue)&value);
  124.     return ConvertODULongFromStd(value);
  125. }
  126.  
  127. //------------------------------------------------------------------------------
  128. // FrameIsInLimbo
  129. //------------------------------------------------------------------------------
  130. //
  131. // It suffices to check containing frames to see if a frame is in-limbo.
  132. // If the source frame of a window becomes "in-limbo", the window must
  133. // be closed, and thus its not possible to access the source frame. [cc]
  134.  
  135. static ODBoolean FrameIsInLimbo(Environment* ev, ODFrame* frame)
  136. {
  137.     ODBoolean isInLimbo = frame->IsInLimbo(ev);
  138.  
  139.     TempODFrame containingFrame = frame->AcquireContainingFrame(ev);
  140.     
  141.     while ( !isInLimbo && (containingFrame != kODNULL) )
  142.     {
  143.         isInLimbo = containingFrame->IsInLimbo(ev);
  144.         ODFrame* nextFrame = containingFrame->AcquireContainingFrame(ev);
  145.         ODReleaseObject(ev, containingFrame);
  146.         containingFrame = nextFrame;
  147.     }
  148.  
  149.     return isInLimbo;
  150. }
  151.  
  152. //======================================================================================
  153. // Class CMIDLink
  154. //======================================================================================
  155.  
  156. class CMIDLink : public Link
  157. {
  158.     public:
  159.         CMIDLink(CMObjectID cmid);
  160.         virtual ~CMIDLink();
  161.  
  162.         CMObjectID GetCMID() { return fCMID; }
  163.  
  164.     private:
  165.         CMObjectID fCMID;
  166. };
  167.  
  168. //------------------------------------------------------------------------------
  169. // CMIDLink Constructor
  170. //------------------------------------------------------------------------------
  171.  
  172. CMIDLink::CMIDLink(CMObjectID cmid)
  173. {
  174.     fCMID = cmid;
  175. }
  176.         
  177. //------------------------------------------------------------------------------
  178. // CMIDLink Destructor
  179. //------------------------------------------------------------------------------
  180.  
  181. CMIDLink::~CMIDLink() 
  182. {
  183. }
  184.  
  185. //=====================================================================================
  186. // Class CMIDList
  187. //=====================================================================================
  188.  
  189. class CMIDList
  190. {
  191.     friend class CMIDListIterator;
  192.     
  193. public:
  194.     CMIDList(ODMemoryHeapID heapID) { fHeapID = heapID; }
  195.     ~CMIDList();
  196.     
  197.     void Add(CMObjectID cmid); 
  198.     void Remove(CMObjectID cmid);
  199.     ODUShort Count();
  200.     CMObjectID First();
  201.     
  202. private:
  203.     LinkedList        fImplementation;
  204.     ODMemoryHeapID    fHeapID;
  205. };
  206.  
  207. //------------------------------------------------------------------------------
  208. // CMIDList Destructor
  209. //------------------------------------------------------------------------------
  210.  
  211. CMIDList::~CMIDList() 
  212. {
  213.     fImplementation.DeleteAllLinks();
  214. }
  215.  
  216. //------------------------------------------------------------------------------
  217. // CMIDList::Add
  218. //------------------------------------------------------------------------------
  219.  
  220. void CMIDList::Add(CMObjectID cmid)
  221. {
  222.     CMIDLink* link = new(fHeapID) CMIDLink(cmid); // "new" THROWS for non-SOM objects
  223.  
  224.     fImplementation.AddFirst((Link*) link);
  225. }
  226.     
  227. //------------------------------------------------------------------------------
  228. // CMIDList::Remove
  229. //------------------------------------------------------------------------------
  230.  
  231. void CMIDList::Remove(CMObjectID cmid)
  232. {
  233.     LinkedListIterator iter(&fImplementation);
  234.  
  235.     for (Link* aLink = iter.First(); 
  236.          iter.IsNotComplete(); 
  237.          aLink = iter.Next())
  238.     {
  239.         CMIDLink* cmidLink = (CMIDLink*) aLink;
  240.         if ( cmidLink->GetCMID() == cmid )
  241.         {
  242.             fImplementation.Remove(*aLink);
  243.             delete cmidLink;
  244.             break;
  245.         }
  246.     }
  247. }
  248.     
  249. //------------------------------------------------------------------------------
  250. // CMIDList::Count
  251. //------------------------------------------------------------------------------
  252.  
  253. ODUShort CMIDList::Count()
  254. {
  255.     return fImplementation.Count();
  256. }
  257.  
  258. //------------------------------------------------------------------------------
  259. // CMIDList::First
  260. //------------------------------------------------------------------------------
  261.  
  262. CMObjectID CMIDList::First()
  263. {
  264.     CMIDLink* cmidLink = (CMIDLink*) fImplementation.First();
  265.     return cmidLink->GetCMID();
  266. }
  267.  
  268. //=====================================================================================
  269. // Class CMIDListIterator
  270. //=====================================================================================
  271.  
  272. class CMIDListIterator
  273. {
  274. public:
  275.     CMIDListIterator(CMIDList* cmidList) : fIterator(&(cmidList->fImplementation)) { }
  276.     ~CMIDListIterator();
  277.     
  278.     CMObjectID First();
  279.     CMObjectID Next();
  280.     ODBoolean IsNotComplete();
  281.     void RemoveCurrent();
  282.     
  283. private:
  284.     LinkedListIterator fIterator;    
  285. };
  286.  
  287. //------------------------------------------------------------------------------
  288. // CMIDListIterator Destructor
  289. //------------------------------------------------------------------------------
  290.  
  291. CMIDListIterator::~CMIDListIterator() 
  292. {
  293. }
  294.  
  295. //------------------------------------------------------------------------------
  296. // CMIDListIterator::First
  297. //------------------------------------------------------------------------------
  298.  
  299. CMObjectID  CMIDListIterator::First()
  300. {
  301.     CMIDLink* cmidLink = (CMIDLink*) fIterator.First();
  302.     if ( cmidLink )
  303.         return cmidLink->GetCMID();
  304.     else
  305.         return kODNULLID;    // There is no "null" container mgr object id,
  306.                             // so this may be a valid id!  Clients must call
  307.                             // IsNotComplete before using result.
  308. }
  309.     
  310. //------------------------------------------------------------------------------
  311. // CMIDListIterator::Next
  312. //------------------------------------------------------------------------------
  313.  
  314. CMObjectID  CMIDListIterator::Next()
  315. {
  316.     CMIDLink* cmidLink = (CMIDLink*) fIterator.Next();
  317.     if ( cmidLink )
  318.         return cmidLink->GetCMID();
  319.     else
  320.         return kODNULLID;    // There is no "null" container mgr object id,
  321.                             // so this may be a valid id!  Clients must call
  322.                             // IsNotComplete before using result.
  323. }
  324.  
  325. //------------------------------------------------------------------------------
  326. // CMIDListIterator::IsNotComplete
  327. //------------------------------------------------------------------------------
  328.  
  329. ODBoolean CMIDListIterator::IsNotComplete()
  330. {
  331.     return fIterator.IsNotComplete();
  332. }
  333.  
  334. //------------------------------------------------------------------------------
  335. // CMIDListIterator::RemoveCurrent
  336. //------------------------------------------------------------------------------
  337.  
  338. void  CMIDListIterator::RemoveCurrent()
  339. {
  340.     fIterator.RemoveCurrent();
  341. }
  342.  
  343. //==============================================================================
  344. // Class CMObjectIDOrList
  345. //==============================================================================
  346.  
  347. class CMObjectIDOrList
  348. {
  349. public:
  350.     CMObjectIDOrList() : fIsEmpty(kODTrue) { }
  351.     CMObjectIDOrList(CMObjectID cmid) : fIsEmpty(kODFalse),
  352.                                         fIsList(kODFalse),
  353.                                         fCMID(cmid) { }
  354.     
  355.     void        Add(CMObjectID cmid, ODMemoryHeapID heapID);
  356.     void        Remove(CMObjectID cmid);
  357.     ODUShort    Count();
  358.     CMObjectID    First();
  359.     CMIDList*    GetCMIDList();
  360.     ODBoolean    IsEmpty() { return fIsEmpty; }
  361.     ODBoolean    IsList() { return fIsList; }
  362.  
  363.     ODBoolean fIsEmpty;
  364.     ODBoolean fIsList;
  365.     union {
  366.         CMObjectID fCMID;
  367.         CMIDList* fCMIDList;
  368.     };
  369. };
  370.  
  371. //------------------------------------------------------------------------------
  372. // CMObjectIDOrList::Add
  373. //------------------------------------------------------------------------------
  374.  
  375. void  CMObjectIDOrList::Add(CMObjectID cmid, ODMemoryHeapID heapID)
  376. {
  377.     WASSERT(!fIsEmpty);
  378.  
  379.     if ( fIsEmpty )
  380.     {
  381.         fCMID = cmid;
  382.         fIsEmpty = kODFalse;
  383.         fIsList = kODFalse;
  384.     }
  385.     else if ( fIsList )
  386.     {
  387.         WASSERT(fCMIDList != kODNULL);
  388.         if ( fCMIDList )
  389.         {
  390.             fCMIDList->Add(cmid);
  391.         }
  392.         else
  393.             THROW(kODErrNullCMIDList);
  394.     }
  395.     else
  396.     {
  397.         CMIDList* cmidList = new(heapID) CMIDList(heapID); // "new" THROWS for non-SOM objects
  398.         TRY
  399.             cmidList->Add(fCMID);
  400.             cmidList->Add(cmid);
  401.         CATCH_ALL
  402.             delete cmidList;
  403.             RERAISE;
  404.         ENDTRY
  405.         fCMIDList = cmidList;
  406.         fIsList = kODTrue;
  407.     }
  408. }
  409.     
  410. //------------------------------------------------------------------------------
  411. // CMObjectIDOrList::Remove
  412. //------------------------------------------------------------------------------
  413.  
  414. void  CMObjectIDOrList::Remove(CMObjectID cmid)
  415. {
  416.     WASSERT(!fIsEmpty);
  417.     if ( fIsEmpty )
  418.         THROW(kODErrEmptyCMObjectIDOrList);
  419.     else if ( fIsList )
  420.     {
  421.         WASSERT(fCMIDList != kODNULL);
  422.         if ( fCMIDList )
  423.         {
  424.             WASSERT(fCMIDList->Count() > 1);
  425.             fCMIDList->Remove(cmid);
  426.             if ( fCMIDList->Count() == 1 )
  427.             {
  428.                 CMObjectID onlyCMID = fCMIDList->First();
  429.                 delete fCMIDList;
  430.                 fCMID = onlyCMID;
  431.                 fIsList = kODFalse;
  432.             }
  433.         }
  434.         else
  435.             THROW(kODErrNullCMIDList);
  436.     }
  437.     else
  438.     {
  439.         WASSERT(cmid == fCMID);
  440.         if ( cmid != fCMID )
  441.             THROW(kODErrCMObjectIDNotFound);
  442.         fIsEmpty = kODTrue;
  443.     }
  444. }
  445.  
  446. //------------------------------------------------------------------------------
  447. // CMObjectIDOrList::Count
  448. //------------------------------------------------------------------------------
  449.  
  450. ODUShort  CMObjectIDOrList::Count()
  451. {
  452.     if ( fIsEmpty )
  453.         return 0;
  454.     else if ( !fIsList )
  455.         return 1;
  456.     else
  457.         return fCMIDList->Count();
  458. }
  459.  
  460. //------------------------------------------------------------------------------
  461. // CMObjectIDOrList::First
  462. //------------------------------------------------------------------------------
  463.  
  464. CMObjectID CMObjectIDOrList::First()
  465. {
  466.     WASSERT(!fIsEmpty);
  467.     if ( fIsEmpty )
  468.     {
  469.         THROW(kODErrEmptyCMObjectIDOrList);
  470.         return kODNULLID;    // not a reliable null value!
  471.     }
  472.     else if ( fIsList )
  473.         return fCMIDList->First();
  474.     else
  475.         return fCMID;
  476. }
  477.  
  478. //------------------------------------------------------------------------------
  479. // CMObjectIDOrList::GetCMIDList
  480. //------------------------------------------------------------------------------
  481.  
  482. CMIDList* CMObjectIDOrList::GetCMIDList()
  483. {
  484.     WASSERT(!fIsEmpty);
  485.     if ( fIsEmpty )
  486.         THROW(kODErrEmptyCMObjectIDOrList);
  487.     if ( !fIsList )
  488.         THROW(kODErrNoCMIDList);
  489.     return fCMIDList;
  490. }
  491.  
  492. //==============================================================================
  493. // ScriptingIDManager
  494. //==============================================================================
  495.  
  496. // Theory of Operation
  497. //
  498. // This class maintains a mapping from scripting IDs to their underlying 
  499. // container manager objects.
  500. //
  501. // CMObjectIDs - Identify container manager objects (i.e. bento objects).
  502. // Persistent across closing and reopening a draft.  Private to the
  503. // container suite (although earlier versions of OpenDoc used these values
  504. // for persistent IDs).
  505. //
  506. // Object IDs - Identify OpenDoc objects.  Based on CMObjectIDs.  Transient,
  507. // cannot be used after a draft has been closed.
  508. //
  509. // Peristent IDs - Also based on CMObjectIDs, but unlike Object IDs these are
  510. // preserved across closing and reopening a draft. Used only for scripting.
  511. //
  512. // Note that there is no CMObjectID value guaranteed to be distinct from any
  513. // valid value.  Since previous versions of OpenDoc exposed CMObjectIDs as
  514. // persistent ids, there is no "null" scripting id.  If an unknown scripting id
  515. // is used to retrieve a persistent object, the scripting id will be interpreted
  516. // as a CMObjectID.
  517. //
  518. // Entries in the table are only eliminated when objects are explicitly removed.
  519. // This is probably ok for frames, which have a remove protocol, but could cause
  520. // obsolete entries for parts to remain.
  521. //
  522. // In the current implementation, GetScriptingID is a little expensive, as
  523. // the table is searched for a matching entry.  This can be sped up if it proves
  524. // to be a performance problem.
  525.  
  526. //------------------------------------------------------------------------------
  527. // ScriptingIDManager::~ScriptingIDManager
  528. //------------------------------------------------------------------------------
  529. ScriptingIDManager::~ScriptingIDManager()
  530. {
  531.     Environment* ev = somGetGlobalEnvironment();
  532.  
  533.     if ( fScriptingTable != kODNULL )
  534.     {
  535.         ODPersistentObjectID sid;
  536.         CMObjectIDOrList cmidOrList;
  537.         OpenHashTableIterator iter(fScriptingTable);
  538.         
  539.         for (iter.First(&sid, &cmidOrList); 
  540.              iter.IsNotComplete();
  541.              iter.Next(&sid, &cmidOrList))
  542.         {
  543.             if ( cmidOrList.IsList() )
  544.             {
  545.                 // Unexpected; undo actions should have committed so
  546.                 // only one object per scripting id should remain.
  547.                 WARN("Multiple objects have scripting id %d", sid);
  548.                 CMIDList* cmidList = cmidOrList.GetCMIDList();
  549.                 delete cmidList;
  550.                 iter.RemoveCurrent();
  551.             }
  552.         }
  553.  
  554.         delete fScriptingTable;
  555.         fScriptingTable = kODNULL;
  556.     }
  557. }
  558.  
  559. //------------------------------------------------------------------------------
  560. // ScriptingIDManager::InitScriptingIDManager
  561. //------------------------------------------------------------------------------
  562. void ScriptingIDManager::InitScriptingIDManager(Environment* ev,
  563.                             ODMemoryHeapID heapID,
  564.                             CMDraft* draft)
  565. {
  566.     fDirty = kODFalse;
  567.     fHeapID = heapID;
  568.     fDraft = draft;
  569.     fLastScriptingID = 0;
  570.  
  571.     ODULong size = 0;
  572.     ODULong count = 0;
  573.  
  574.     TRY
  575.         TempODStorageUnit su = kODNULL;
  576.         TempODStorageUnit draftPropertiesSU = fDraft->AcquireDraftProperties(ev);
  577.         ODStorageUnitID id = ODGetWeakSURefProp(ev, draftPropertiesSU, 
  578.                                     kODPropScriptingIDs, kODWeakStorageUnitRef);
  579.  
  580.         if ( id != kODNULLID )
  581.         {
  582.             su = fDraft->AcquireStorageUnit(ev, id);
  583.  
  584.             if ( ODSUExistsThenFocus(ev, su, kODPropScriptingIDs, kODULongSequence) )
  585.             {
  586.                 size = su->GetSize(ev);
  587.                 if ( size > kODULongLen )
  588.                     count = (size - kODULongLen) / (kODULongLen+kODULongLen);
  589.             }
  590.         }
  591.  
  592.         fScriptingTable =
  593.             new(heapID) OpenHashTable(OpenHashTable::StdEqual,
  594.                                       OpenHashTable::StdHash,
  595.                                       heapID);  // "new" THROWS for non-SOM objects
  596.         fScriptingTable->Initialize(kODInitialNumEntries+count,
  597.                                     sizeof(ODPersistentObjectID),
  598.                                     sizeof(CMObjectIDOrList));
  599.  
  600.         if ( size > 0 )
  601.         {
  602.             su->SetOffset(ev, 0);
  603.  
  604.             // First long is the greatest scripting id assigned.
  605.             fLastScriptingID = GetULongFromFocusedSU(su, ev);
  606.  
  607.             if ( count > 0 )
  608.             {
  609.                 ODPersistentObjectID scriptingID;
  610.                 CMObjectIDOrList cmidOrList;
  611.                 CMObjectID cmid;
  612.                 
  613.                 do
  614.                 {
  615.                     scriptingID = GetULongFromFocusedSU(su, ev);
  616.                     cmid = GetULongFromFocusedSU(su, ev);
  617.  
  618.                     WASSERT(scriptingID < kFirstCMObjectID);
  619.  
  620.                     if ( scriptingID > fLastScriptingID )
  621.                     {
  622.                         WARN("Object's scripting id %d is beyond expected limit of %d", scriptingID, fLastScriptingID);
  623.                         fLastScriptingID = scriptingID;
  624.                     }
  625.  
  626.                     if ( fScriptingTable->GetValue(&scriptingID, &cmidOrList) )
  627.                     {
  628.                         WARN("Multiple objects have scripting id %d", scriptingID);
  629.                         cmidOrList.Add(cmid, fHeapID);
  630.                     }
  631.                     else
  632.                     {
  633.                         cmidOrList.fCMID = cmid;
  634.                         cmidOrList.fIsList = kODFalse;
  635.                         cmidOrList.fIsEmpty = kODFalse;
  636.                     }
  637.                     
  638.                     fScriptingTable->ReplaceEntry(&scriptingID, &cmidOrList);
  639.                 }
  640.                 while ( --count > 0 );
  641.             }
  642.         }
  643.     CATCH_ALL
  644.         delete fScriptingTable;
  645.         fScriptingTable = kODNULL;
  646.         RERAISE;
  647.     ENDTRY
  648. }
  649.  
  650.  
  651. //------------------------------------------------------------------------------
  652. // ScriptingIDManager::SearchForScriptingID
  653. //------------------------------------------------------------------------------
  654. ODBoolean ScriptingIDManager::SearchForScriptingID(
  655.                             Environment *ev,
  656.                             CMObjectID objectID,
  657.                             ODPersistentObjectID& scriptingID)
  658. {
  659.     // Search for the persistent ID associated with the argument cm object.
  660.     // This is an expensive way to get a persistent ID, but might not be
  661.     // bad overall if persistent IDs are only requested for a small percentage
  662.     // of frames and parts.  If performance is a problem, and multiple requests
  663.     // are made for the same objects, we could cache each cmid-scriptingID pair
  664.     // as they are looked up.
  665.     // Two alternatives to this lookup: (1) When the draft is opened, create a
  666.     // second table that maps from cmid to persistent id.  This table would 
  667.     // contain an entry for each part and frame created in the document.
  668.     // (2) Store the persistent ID in the storage unit.  The problem here is
  669.     // that CMStorageUnit::CloneInto will clone the persistent id property,
  670.     // which is not desired.  That could be prevented by modifying CMStorageUnit,
  671.     // but that couples another class into this issue.
  672.  
  673.     ODBoolean found = kODFalse;
  674.     ODPersistentObjectID sid;
  675.  
  676.     CMObjectIDOrList cmidOrList;
  677.     OpenHashTableIterator iter(fScriptingTable);
  678.     
  679.     for (iter.First(&sid, &cmidOrList); 
  680.          !found && iter.IsNotComplete();
  681.          iter.Next(&sid, &cmidOrList))
  682.     {
  683.         if ( cmidOrList.IsList() )
  684.         {
  685.             CMIDListIterator iter(cmidOrList.GetCMIDList());
  686.             for (CMObjectID cmid = iter.First();
  687.                 !found && iter.IsNotComplete();
  688.                 cmid = iter.Next())
  689.             {
  690.                 if ( cmid == objectID )
  691.                 {
  692.                     found = kODTrue;
  693.                     scriptingID = sid;
  694.                 }
  695.             }
  696.         }
  697.         else
  698.         {
  699.              if ( cmidOrList.fCMID == objectID )
  700.              {
  701.                  found = kODTrue;
  702.                 scriptingID = sid;
  703.             }
  704.         }
  705.     }
  706.  
  707.     return found;
  708. }
  709.  
  710. //------------------------------------------------------------------------------
  711. // ScriptingIDManager::GetScriptingID
  712. //------------------------------------------------------------------------------
  713. ODPersistentObjectID ScriptingIDManager::GetScriptingID(
  714.                             Environment *ev,
  715.                             CMObjectID objectID)
  716. {
  717.     ODPersistentObjectID scriptingID;
  718.  
  719.     if ( !this->SearchForScriptingID(ev, objectID, scriptingID) )
  720.         scriptingID = this->AssignScriptingID(ev, objectID);
  721.  
  722.     return scriptingID;
  723. }
  724.  
  725. //------------------------------------------------------------------------------
  726. // ScriptingIDManager::AcquirePersistentObject
  727. //------------------------------------------------------------------------------
  728.  
  729. ODPersistentObject* ScriptingIDManager::AcquirePersistentObject(
  730.                             Environment *ev,
  731.                             ODPersistentObjectID scriptingID,
  732.                             ODObjectType* objectType)
  733. {
  734.     CMObjectIDOrList cmidOrList;
  735.     ODPersistentObject* object = kODNULL;
  736.  
  737.     if ( fScriptingTable->GetValue(&scriptingID, &cmidOrList) )
  738.     {
  739.         if ( cmidOrList.IsList() )
  740.         {
  741.             CMIDList* cmidList = cmidOrList.GetCMIDList();
  742.  
  743.             // Begin a new scope for iter
  744.             {
  745.                 ODBoolean skip = kODFalse;
  746.  
  747.                 CMIDListIterator iter(cmidList);
  748.                 for (CMObjectID cmid = iter.First();
  749.                     iter.IsNotComplete();
  750.                     cmid = iter.Next())
  751.                 {
  752.                     TempODFrame frame = kODNULL;
  753.  
  754.                     // If the table contains an invalid entry, remove the cmid from
  755.                     // the linked list and continue the iteration.  Note that this
  756.                     // may cause the list to contain fewer than 2 entries.
  757.                     TRY
  758.                         frame = (ODFrame*) fDraft->AcquirePersistentObjectByCMObjectID(ev, cmid, objectType);
  759.                     CATCH_ALL
  760.                         if ( ErrorCode() == kODErrInvalidPersistentObjectID )
  761.                         {
  762.                             WARN("Invalid object %d associated with scripting ID %d", cmid, scriptingID);
  763.                             iter.RemoveCurrent();
  764.                             skip = kODTrue;
  765.                         }
  766.                         else
  767.                             RERAISE;
  768.                     ENDTRY
  769.  
  770.                     if ( skip )
  771.                     {
  772.                         skip = kODFalse;
  773.                         continue;
  774.                     }
  775.  
  776.                     if ( ODISOStrEqual(*objectType, kODFrameObject) )
  777.                     {
  778.                         if ( !FrameIsInLimbo(ev, frame) )
  779.                         {
  780. #ifdef ODDebug_ScriptingIDs
  781.                             if ( object != kODNULL )
  782.                             {
  783.                                 WARN("Multiple frames with the same scripting ID are not in-limbo!");
  784.                             }
  785.                             else
  786.                             {
  787.                                 object = frame;
  788.                                 frame.DontRelease();
  789.                             }
  790. #else
  791.                             object = frame;
  792.                             frame.DontRelease();
  793.                             break;
  794. #endif
  795.                         }
  796.                     }
  797.                     else
  798.                     {
  799.                         THROW(kODErrInvalidObjectType);
  800.                     }
  801.                 }
  802.             }
  803.  
  804.             // If the list contained invalid entries, it may be too short
  805.             if ( cmidList->Count() == 0 )
  806.             {
  807.                 fScriptingTable->RemoveEntry(&scriptingID);
  808.                 fDirty = kODTrue;
  809.                 delete cmidList;
  810.             }
  811.             else if ( cmidList->Count() == 1 )
  812.             {
  813.                 cmidOrList.fCMID = cmidList->First();
  814.                 cmidOrList.fIsList = kODFalse;
  815.                 fScriptingTable->ReplaceEntry(&scriptingID, &cmidOrList);
  816.                 fDirty = kODTrue;
  817.                 delete cmidList;
  818.             }
  819.  
  820.             if ( object == kODNULL )
  821.             {
  822.                 // All table entries with the scriptingID are in-limbo.
  823.                 // This is valid if a frame is moved out of the document.
  824.                 THROW(kODErrInvalidPersistentObjectID);
  825.             }
  826.         }
  827.         else
  828.         {
  829.                 // If the table contains an invalid entry, remove it!
  830.                 TRY
  831.                     object = fDraft->AcquirePersistentObjectByCMObjectID(ev, cmidOrList.First(), objectType);
  832.                 CATCH_ALL
  833.                     if ( ErrorCode() == kODErrInvalidPersistentObjectID )
  834.                     {
  835.                         WARN("Invalid object %d is no longer associated with scripting ID %d", cmidOrList.First(), scriptingID);
  836.                         // Just remove the entry from the table, since 
  837.                         // cmidOrList contains a single cmid.
  838.                         fScriptingTable->RemoveEntry(&scriptingID);
  839.                     }
  840.                     RERAISE;
  841.                 ENDTRY
  842.         }
  843.     }
  844.     else
  845.     {
  846.         object = fDraft->AcquirePersistentObjectByCMObjectID(ev, scriptingID, objectType);
  847.     }
  848.  
  849.     return object;
  850. }
  851.  
  852. //------------------------------------------------------------------------------
  853. // ScriptingIDManager::AssignScriptingID
  854. //------------------------------------------------------------------------------
  855.  
  856. ODPersistentObjectID ScriptingIDManager::AssignScriptingID(
  857.                             Environment* ev,
  858.                             CMObjectID cmid)
  859. {
  860.     CMObjectIDOrList cmidOrList(cmid);
  861.  
  862.     ++fLastScriptingID;
  863.  
  864.     fScriptingTable->ReplaceEntry(&fLastScriptingID, &cmidOrList);
  865.  
  866.     fDirty = kODTrue;
  867.  
  868.     return fLastScriptingID;
  869. }
  870.  
  871. //------------------------------------------------------------------------------
  872. // ScriptingIDManager::TransferScriptingID
  873. //------------------------------------------------------------------------------
  874.  
  875. ODPersistentObjectID ScriptingIDManager::TransferScriptingID(
  876.                             Environment* ev,
  877.                             CMObjectID fromCMID,
  878.                             CMObjectID toCMID)
  879. {
  880.     // We loose backward compatability here with scripts that use
  881.     // old persistent IDs, since we transfer new scripting IDs.
  882.     ODPersistentObjectID scriptingID = GetScriptingID(ev, fromCMID);
  883.  
  884. #ifdef ODDebug_ScriptingIDs
  885.     PRINT("Transferring scripting id %d from object %d to %d\n", scriptingID, fromCMID, toCMID);
  886. #endif
  887.  
  888.     CMObjectIDOrList cmidOrList;
  889.     if ( fScriptingTable->GetValue(&scriptingID, &cmidOrList) )
  890.     {
  891.         cmidOrList.Add(toCMID, fHeapID);
  892.         fScriptingTable->ReplaceEntry(&scriptingID, &cmidOrList);
  893.         fDirty = kODTrue;
  894.     }
  895.     else
  896.     {
  897.         // Unexpected condition.  Rather than fail, just assign a new scripting ID
  898.         WARN("Transfer of scripting id %d to object %d failed", scriptingID, toCMID);
  899.         scriptingID = GetScriptingID(ev, toCMID);
  900.     }
  901.     return scriptingID;
  902. }
  903.  
  904. //------------------------------------------------------------------------------
  905. // ScriptingIDManager::DropScriptingID
  906. //------------------------------------------------------------------------------
  907.  
  908. void ScriptingIDManager::DropScriptingID(Environment* ev,
  909.                             CMObjectID cmid)
  910. {
  911. #ifdef ODDebug_ScriptingIDs
  912.     PRINT("Droppinging scripting id for object %d\n", cmid);
  913. #endif
  914.  
  915.     ODPersistentObjectID scriptingID;
  916.     if ( this->SearchForScriptingID(ev, cmid, scriptingID) )
  917.     {
  918.         CMObjectIDOrList cmidOrList;
  919.         if ( fScriptingTable->GetValue(&scriptingID, &cmidOrList) )
  920.         {
  921.             cmidOrList.Remove(cmid);
  922.             if ( cmidOrList.IsEmpty() )
  923.                 fScriptingTable->RemoveEntry(&scriptingID);
  924.             else
  925.                 fScriptingTable->ReplaceEntry(&scriptingID, &cmidOrList);
  926.         }
  927.  
  928.         fDirty = kODTrue;
  929.     }
  930. }
  931.  
  932. //------------------------------------------------------------------------------
  933. // ScriptingIDManager::Save
  934. //------------------------------------------------------------------------------
  935.  
  936. void ScriptingIDManager::Save(Environment* ev)
  937. {
  938.     if ( fDirty )
  939.     {
  940.         TempODStorageUnit su = kODNULL;
  941.         TempODStorageUnit draftPropertiesSU = fDraft->AcquireDraftProperties(ev);
  942.  
  943.         ODStorageUnitID id = ODGetWeakSURefProp(ev, draftPropertiesSU, 
  944.                                     kODPropScriptingIDs, kODWeakStorageUnitRef);
  945.  
  946.         if ( id == kODNULLID )
  947.         {
  948.             su = fDraft->CreateStorageUnit(ev);
  949.             id = su->GetID(ev);
  950.             ODSetWeakSURefProp(ev, draftPropertiesSU, kODPropScriptingIDs, 
  951.                                             kODWeakStorageUnitRef, id);
  952.         }
  953.         else
  954.         {
  955.             su = fDraft->AcquireStorageUnit(ev, id);
  956.         }
  957.         
  958.         ODSUForceFocus(ev, su, kODPropScriptingIDs, kODULongSequence);
  959.         su->SetOffset(ev, 0);
  960.         ODULong oldSize = su->GetSize(ev);
  961.  
  962.         // First long is the greatest scripting id assigned.
  963.         WriteULongToFocusedSU(su, ev, fLastScriptingID);
  964.  
  965.         ODPersistentObjectID scriptingID;
  966.         CMObjectIDOrList cmidOrList;
  967.         OpenHashTableIterator iter(fScriptingTable);
  968.         
  969.         for (iter.First(&scriptingID, &cmidOrList); 
  970.              iter.IsNotComplete();
  971.              iter.Next(&scriptingID, &cmidOrList))
  972.         {
  973.             if ( cmidOrList.IsList() )
  974.             {
  975.                 // When this method is called, the undo stack must be committed,
  976.                 // so in theory there should be no lin-limbo frames and hense
  977.                 // no lists in the table.
  978.                 // Just to be safe, dump out the list anyway.
  979.                 WARN("There are %d objects assigned the same scripting ID %d", cmidOrList.Count(), scriptingID);
  980.                 CMIDListIterator iter(cmidOrList.GetCMIDList());
  981.                 for (CMObjectID cmid = iter.First();
  982.                     iter.IsNotComplete();
  983.                     cmid = iter.Next())
  984.                 {
  985.                     WriteULongToFocusedSU(su, ev, scriptingID);
  986.                     WriteULongToFocusedSU(su, ev, cmid);
  987. #ifdef ODDebug_ScriptingIDs
  988.                     PRINT("%d ", cmid);
  989. #endif
  990.                 }
  991. #ifdef ODDebug_ScriptingIDs
  992.                 PRINT("\n");
  993. #endif
  994.             }
  995.             else
  996.             {
  997.                 WriteULongToFocusedSU(su, ev, scriptingID);
  998.                 WriteULongToFocusedSU(su, ev, cmidOrList.fCMID);
  999.             }
  1000.         }
  1001.  
  1002.         ODULong    newSize = su->GetOffset(ev);
  1003.         if ( oldSize > newSize )
  1004.             su->DeleteValue(ev, oldSize - newSize);
  1005.  
  1006.         fDirty = kODFalse;
  1007.     }
  1008. }
  1009.